home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / xulrunner / python / selection.py < prev    next >
Encoding:
Python Source  |  2007-11-12  |  23.6 KB  |  633 lines

  1. # Miro - an RSS based video player application
  2. # Copyright (C) 2005-2007 Participatory Culture Foundation
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  17.  
  18. """Handle selection."""
  19.  
  20. from copy import copy
  21.  
  22. import app
  23. import database
  24. import eventloop
  25. import folder
  26. import guide
  27. import item
  28. import tabs
  29. import playlist
  30. import feed
  31. import views
  32. import template
  33. import util
  34. from gtcache import gettext as _
  35.  
  36. def getID(obj):
  37.     """Gets an ID to use for an object.  For tabs, this is the object ID that
  38.     maps to the tab.  For other objects this is the actual DDBObject ID."""
  39.     if isinstance(obj, tabs.Tab):
  40.         return obj.objID()
  41.     else:
  42.         return obj.getID()
  43.  
  44. class SelectionArea(object):
  45.     """Represents an area that holds a selection.  Currently we have 2
  46.     SelectionAreas, the tab list and the item list.  SelectionAreas hold
  47.     several database views, for instance the tab list contains
  48.     views.guideTabs, views.staticTabs, views.feedTabs and views.playlistTabs.
  49.     All the items selected in an area must be in a single view.
  50.  
  51.     Member variables:
  52.  
  53.     currentView -- The view that items are currently selected in, or None if
  54.         there are no items selected.
  55.     currentSelection -- set of object IDs that are currently selected.
  56.     """
  57.  
  58.     def __init__(self, selectionHandler):
  59.         self.currentSelection = set()
  60.         self.currentView = None
  61.         self.handler = selectionHandler
  62.  
  63.     def switchView(self, view):
  64.         if self.currentView == view:
  65.             return
  66.         if self.currentView:
  67.             self.clearSelection()
  68.         self.currentView = view
  69.         self.currentView.addRemoveCallback(self.onRemove)
  70.         self.currentView.addAddCallback(self.onAdd)
  71.         self.currentView.addViewUnlinkCallback(self.onViewUnlinked)
  72.  
  73.     def selectItem(self, view, id):
  74.         self.switchView(view)
  75.         obj = view.getObjectByID(id)
  76.         self.currentSelection.add(id)
  77.         obj.setSelected(True)
  78.  
  79.     def deselectItem(self, view, id):
  80.         if view != self.currentView:
  81.             raise ValueError("view != current view in deselectItem()")
  82.         obj = view.getObjectByID(id)
  83.         self.currentSelection.remove(id)
  84.         obj.setSelected(False)
  85.  
  86.     def isSelected(self, view, id):
  87.         return self.currentView == view and id in self.currentSelection
  88.  
  89.     def toggleItemSelect(self, view, id):
  90.         self.switchView(view)
  91.         if id in self.currentSelection:
  92.             self.deselectItem(view, id)
  93.         else:
  94.             self.selectItem(view, id)
  95.  
  96.     def clearSelection(self):
  97.         """Clears the current selection."""
  98.  
  99.         for obj in self.getObjects():
  100.             obj.setSelected(False)
  101.         self.currentSelection = set()
  102.         if self.currentView is not None:
  103.             self.currentView.removeRemoveCallback(self.onRemove)
  104.             self.currentView.removeAddCallback(self.onAdd)
  105.             self.currentView = None
  106.  
  107.     def selectAll(self):
  108.         """Select all objects in the current View."""
  109.         
  110.         view = self.currentView
  111.         if view:
  112.             for obj in view:
  113.                 if obj.getID not in self.currentSelection:
  114.                     self.selectItem(view, obj.getID())
  115.  
  116.     def calcExtendRange(self, view, id):
  117.         idIsBefore = False
  118.         gotFirst = False
  119.         firstID = lastID = None
  120.         for obj in view:
  121.             objID = getID(obj)
  122.             if objID == id and not gotFirst:
  123.                 idIsBefore = True
  124.             if objID in self.currentSelection:
  125.                 if not gotFirst:
  126.                     firstID = objID
  127.                     gotFirst = True
  128.                 lastID = objID
  129.         if firstID is None or lastID is None:
  130.             raise AssertionError("Couldn't find my selected IDs")
  131.         if idIsBefore:
  132.             return id, lastID
  133.         else:
  134.             return firstID, id
  135.  
  136.     def extendSelection(self, view, id):
  137.         """Extends the selection in response to a shift-select.  If id is on
  138.         top of the current selection, we will select everything between the id
  139.         and the last selected item.  If id is below it or in the middle, we
  140.         will select between the first selected item and id.  
  141.         """
  142.  
  143.         self.switchView(view)
  144.         if len(self.currentSelection) == 0:
  145.             return self.selectItem(view, id)
  146.         firstID, lastID = self.calcExtendRange(view, id)
  147.         self.selectBetween(view, firstID, lastID)
  148.  
  149.     def selectBetween(self, view, firstID, lastID):
  150.         """Select all items in view between firstID and lastID."""
  151.  
  152.         self.switchView(view)
  153.         selecting = False
  154.         toSelect = []
  155.         for obj in view:
  156.             id = getID(obj)
  157.             if selecting and id not in self.currentSelection:
  158.                 toSelect.append(id)
  159.             if id == firstID:
  160.                 selecting = True
  161.                 if id not in self.currentSelection:
  162.                     toSelect.append(id)
  163.             if id == lastID:
  164.                 break
  165.         for id in toSelect:
  166.             self.selectItem(view, id)
  167.  
  168.     def onRemove(self, obj, id):
  169.         if id in self.currentSelection:
  170.             self.currentSelection.remove(id)
  171.             if obj.idExists():
  172.                 obj.setSelected(False)
  173.  
  174.     def setObjectsActive(self, newValue):
  175.         """Iterate through all selected objects and call setActive on them,
  176.         passing in newValue.
  177.         """
  178.  
  179.         for obj in self.getObjects():
  180.             obj.setActive(newValue)
  181.  
  182.     def onAdd(self, obj, id):
  183.         if obj.getSelected() and id not in self.currentSelection:
  184.             # this happens when we remove/add the object to reorder it in a
  185.             # playlist
  186.             self.currentSelection.add(id)
  187.  
  188.     def onViewUnlinked(self):
  189.         self.clearSelection()
  190.  
  191.     def getTypesDetailed(self):
  192.         """Get the type of objects that are selected.  
  193.  
  194.         Returns a set, containing all the type of objects selected.  The
  195.         members will be one of the following:
  196.  
  197.         'item', 'downloadeditem' 'playlisttab', playlistfoldertab,
  198.         'channeltab', 'channelfoldertab', 'guidetab', 'addedguidetab',
  199.         'statictab'.
  200.  
  201.         """
  202.  
  203.         types = set()
  204.         for obj in self.getObjects():
  205.             if isinstance(obj, item.Item):
  206.                 if obj.isDownloaded():
  207.                     newType = 'downloadeditem'
  208.                 else:
  209.                     newType = 'item'
  210.             elif isinstance(obj, tabs.Tab):
  211.                 objClass = obj.obj.__class__
  212.                 if objClass == playlist.SavedPlaylist:
  213.                     newType = 'playlisttab'
  214.                 elif objClass == folder.PlaylistFolder:
  215.                     newType = 'playlistfoldertab'
  216.                 elif objClass == feed.Feed:
  217.                     newType = 'channeltab'
  218.                 elif objClass == folder.ChannelFolder:
  219.                     newType = 'channelfoldertab'
  220.                 elif objClass == guide.ChannelGuide:
  221.                     if obj.obj.getDefault():
  222.                         newType = 'guidetab'
  223.                     else:
  224.                         newType = 'addedguidetab'
  225.                 elif objClass == tabs.StaticTab:
  226.                     newType = 'statictab'
  227.                 else:
  228.                     raise ValueError("Bad selected tab type: %s" % obj.obj)
  229.             else:
  230.                 raise ValueError("Bad selected object type: %s" % obj)
  231.             types.add(newType)
  232.         self.simplifyTypes(types) 
  233.         # we don't care about the result of simplifyTypes, but the error
  234.         # checking is useful
  235.         return types
  236.  
  237.     def simplifyTypes(self, types):
  238.         if len(types) == 0:
  239.             return None
  240.         elif types.issubset(set(["item", "downloadeditem"])):
  241.                 return "item"
  242.         elif types.issubset(set(["playlistfoldertab", "playlisttab"])):
  243.                 return "playlisttab"
  244.         elif types.issubset(set(["channelfoldertab", "channeltab"])):
  245.                 return "channeltab"
  246.         elif len(types) == 1:
  247.             for type in types:
  248.                 return type
  249.         else:
  250.             raise ValueError("Multiple types selected: %s" % types)
  251.  
  252.     def getType(self):
  253.         """Get the simplified version of the type of objects that are
  254.         selected.  getType() works like getTypesDetailed(), but it just
  255.         returns one value.  It doesn't differentiate between playlists and
  256.         playlist folders, and items and downloaded items for example.
  257.  
  258.         The return value will be one of
  259.  
  260.         "item", "playlisttab", "channeltab", 'guidetab', 'addedguidetab',
  261.         'statictab', or None if nothing is selected.  
  262.  
  263.         """
  264.  
  265.         return self.simplifyTypes(self.getTypesDetailed())
  266.  
  267.     def getObjects(self):
  268.         view = self.currentView
  269.         return [view.getObjectByID(id) for id in self.currentSelection]
  270.  
  271.     def firstBeforeSelection(self, iterator):
  272.         """Go through iterator and find the first item that is selected.
  273.         Returns the item immediately before that one.
  274.  
  275.         Returns None if the first item in iterator is selected, or no items
  276.         are selected.
  277.         """
  278.  
  279.         lastItem = None
  280.         for item in iterator:
  281.             if item.getID() in self.currentSelection:
  282.                 return lastItem
  283.             lastItem = item
  284.         return None
  285.  
  286.     def firstAfterSelection(self, iterator):
  287.         """Like firstBeforeSelection, but returns the first item following the
  288.         last selected item in iterator.
  289.         """
  290.  
  291.         retval = None
  292.         lastSelected = False
  293.         for item in iterator:
  294.             if item.getID() in self.currentSelection:
  295.                 lastSelected = True
  296.             elif lastSelected:
  297.                 lastSelected = False
  298.                 retval = item
  299.         return retval
  300.  
  301. class TabSelectionArea(SelectionArea):
  302.     """Selection area for the tablist.  This has a couple special cases to
  303.     ensure that we always have at least one tab selected.
  304.     """
  305.  
  306.     def selectItem(self, view, id):
  307.         SelectionArea.selectItem(self, view, id)
  308.         self.moveCursorToSelection()
  309.  
  310.     def deselectItem(self, view, id):
  311.         SelectionArea.deselectItem(self, view, id)
  312.         self.moveCursorToSelection()
  313.  
  314.     def moveCursorToSelection(self):
  315.         for id in self.currentSelection:
  316.             self.currentView.moveCursorToID(id)
  317.             break
  318.  
  319.     def toggleItemSelect(self, view, id):
  320.         # Don't let a control select deselect the last selected item in the
  321.         # tab list.
  322.         if self.currentSelection == set([id]):
  323.             return
  324.         else:
  325.             return SelectionArea.toggleItemSelect(self, view, id)
  326.  
  327.     def onRemove(self, obj, id):
  328.         SelectionArea.onRemove(self, obj, id)
  329.         # We may be removing/adding tabs quickly to reorder them.  Use an idle
  330.         # callback to check if none are selected so we do the Right Thing in
  331.         # this case.
  332.         eventloop.addUrgentCall(self.checkNoTabsSelected,
  333.                 "checkNoTabsSelected")
  334.  
  335.     def checkNoTabsSelected(self):
  336.         if len(self.currentSelection) == 0:
  337.             prevTab = self.currentView.cur()
  338.             if prevTab is None:
  339.                 # we remove the 1st tab in the list, try to select the new 1st
  340.                 # tab
  341.                 prevTab = self.currentView.getNext()
  342.             if prevTab is None:
  343.                 # That was the last tab in the list, select the guide
  344.                 self.selectFirstGuide()
  345.             else:
  346.                 self.selectItem(self.currentView, prevTab.objID())
  347.             self.handler.displayCurrentTabContent()
  348.  
  349.     def selectFirstGuide(self):
  350.         views.guideTabs.resetCursor()
  351.         guide = views.guideTabs.getNext()
  352.         self.selectItem(views.guideTabs, guide.objID())
  353.         self.handler.displayCurrentTabContent()
  354.  
  355.     def isFolderSelected(self):
  356.         """Returns if a channel/playlist folder is selected."""
  357.         for tab in self.getObjects():
  358.             if isinstance(tab.obj, folder.FolderBase):
  359.                 return True
  360.         return False
  361.  
  362. class SelectionHandler(object):
  363.     """Handles selection for Democracy.
  364.  
  365.     Attributes:
  366.  
  367.     tabListSelection -- SelectionArea for the tab list
  368.     itemListSelection -- SelectionArea for the item list
  369.     tabListActive -- does the tabListSelection the have the "active"
  370.         selection?  In other words, is that the one that was clicked on last.
  371.     """
  372.  
  373.     def __init__(self):
  374.         self.tabListSelection = TabSelectionArea(self)
  375.         self.itemListSelection = SelectionArea(self)
  376.         self.lastDisplay = None
  377.         self.tabListActive = True
  378.  
  379.     def getSelectionForArea(self, area):
  380.         if area == 'tablist':
  381.             return self.tabListSelection
  382.         elif area == 'itemlist':
  383.             return self.itemListSelection
  384.         else:
  385.             raise ValueError("Unknown area: %s" % area)
  386.  
  387.     def isSelected(self, area, view, id):
  388.         return self.getSelectionForArea(area).isSelected(view, id)
  389.  
  390.     def selectItem(self, area, view, id, shiftSelect, controlSelect, displayTabContent=True):
  391.         selection = self.getSelectionForArea(area)
  392.         try:
  393.             selectedObj = view.getObjectByID(id)
  394.         except database.ObjectNotFoundError:
  395.             # Item got deleted before the select went through.
  396.             return
  397.  
  398.         # ignore control and shift when selecting static tabs
  399.         if (isinstance(selectedObj, tabs.Tab) and 
  400.                 selectedObj.type in ('statictab', 'guide')):
  401.             controlSelect = shiftSelect = False
  402.  
  403.         if controlSelect:
  404.             selection.toggleItemSelect(view, id)
  405.         elif shiftSelect:
  406.             selection.extendSelection(view, id)
  407.         else:
  408.             selection.clearSelection()
  409.             selection.selectItem(view, id)
  410.  
  411.         if area == 'itemlist':
  412.             self.setTabListActive(False)
  413.             self.updateMenus()
  414.         else:
  415.             self.setTabListActive(True)
  416.             if displayTabContent:
  417.                 self.displayCurrentTabContent()
  418.  
  419.     def setTabListActive(self, value):
  420.         self.tabListActive = value
  421.         self.tabListSelection.setObjectsActive(value)
  422.         self.itemListSelection.setObjectsActive(not value)
  423.  
  424.     def calcSelection(self, area, sourceID):
  425.         """Calculate the selection, given the ID of an object that was clicked
  426.         on.  If sourceID is in the current selection, this will all the
  427.         objects in the current selection, otherwise it will be only the object
  428.         that corresponds to sourceID.  
  429.         """
  430.  
  431.         selection = self.getSelectionForArea(area)
  432.         if sourceID in selection.currentSelection:
  433.             return set(selection.currentSelection)
  434.         else:
  435.             return set([sourceID])
  436.  
  437.     def selectFirstGuide(self):
  438.         self.tabListSelection.selectFirstGuide()
  439.  
  440.     def selectTabByTemplateBase(self, tabTemplateBase, displayTabContent=True):
  441.         tabViews = [ 
  442.             views.guideTabs, 
  443.             views.staticTabs, 
  444.             views.feedTabs, 
  445.             views.playlistTabs,
  446.         ]
  447.         for view in tabViews:
  448.             for tab in view:
  449.                 if tab.tabTemplateBase == tabTemplateBase:
  450.                     self.selectItem('tablist', view, tab.objID(),
  451.                             shiftSelect=False, controlSelect=False,
  452.                             displayTabContent=displayTabContent)
  453.                     return
  454.  
  455.     def selectTabByObject(self, obj, displayTabContent=True):
  456.         channelTabOrder = util.getSingletonDDBObject(views.channelTabOrder)
  457.         playlistTabOrder = util.getSingletonDDBObject(views.playlistTabOrder)
  458.         tabViews = [ 
  459.             views.guideTabs, 
  460.             views.staticTabs, 
  461.             channelTabOrder.getView(), 
  462.             playlistTabOrder.getView(), 
  463.         ]
  464.         for view in tabViews:
  465.             for tab in view:
  466.                 if tab.obj is obj:
  467.                     self.selectItem('tablist', view, tab.objID(),
  468.                             shiftSelect=False, controlSelect=False,
  469.                             displayTabContent=displayTabContent)
  470.                     return
  471.  
  472.     def _chooseDisplayForCurrentTab(self):
  473.         tls = self.tabListSelection
  474.         frame = app.controller.frame
  475.  
  476.         if len(tls.currentSelection) == 0:
  477.             raise AssertionError("No tabs selected")
  478.         elif len(tls.currentSelection) == 1:
  479.             for id in tls.currentSelection:
  480.                 tab = tls.currentView.getObjectByID(id)
  481.                 return app.TemplateDisplay(tab.contentsTemplate,
  482.                                            tab.templateState,
  483.                         frameHint=frame, areaHint=frame.mainDisplay, 
  484.                         id=tab.obj.getID())
  485.         else:
  486.             foldersSelected = False
  487.             type = tls.getType()
  488.             if type == 'playlisttab':
  489.                 templateName = 'multi-playlist'
  490.             elif type == 'channeltab':
  491.                 templateName = 'multi-channel'
  492.             selectedChildren = 0
  493.             selectedFolders = 0
  494.             containedChildren = 0
  495.             for tab in self.getSelectedTabs():
  496.                 if isinstance(tab.obj, folder.FolderBase):
  497.                     selectedFolders += 1
  498.                     view = tab.obj.getChildrenView()
  499.                     containedChildren += view.len()
  500.                     for child in view:
  501.                         if child.getID() in tls.currentSelection:
  502.                             selectedChildren -= 1
  503.                 else:
  504.                     selectedChildren += 1
  505.             return app.TemplateDisplay(templateName,'default', frameHint=frame,
  506.                     areaHint=frame.mainDisplay,
  507.                     selectedFolders=selectedFolders,
  508.                     selectedChildren=selectedChildren,
  509.                     containedChildren=containedChildren)
  510.  
  511.     def updateMenus(self):
  512.         tabTypes = self.tabListSelection.getTypesDetailed()
  513.         if tabTypes.issubset(set(['guidetab', 'addedguidetab'])):
  514.             guideURL = self.getSelectedTabs()[0].obj.getURL()
  515.         else:
  516.             guideURL = None
  517.         multiple = len(self.tabListSelection.currentSelection) > 1
  518.  
  519.         actionGroups = {}
  520.         states = {"plural":[],
  521.                   "folders":[],
  522.                   "folder":[]}
  523.  
  524.         is_playlistlike = tabTypes.issubset (set(['playlisttab', 'playlistfoldertab']))
  525.         is_channellike = tabTypes.issubset (set(['channeltab', 'channelfoldertab', 'addedguidetab']))
  526.         is_channel = tabTypes.issubset (set(['channeltab', 'channelfoldertab']))
  527.         if len (tabTypes) == 1:
  528.             if multiple:
  529.                 if 'playlisttab' in tabTypes:
  530.                     states["plural"].append("RemovePlaylists")
  531.                 elif 'playlistfoldertab' in tabTypes:
  532.                     states["folders"].append("RemovePlaylists")
  533.                 elif 'channeltab' in tabTypes:
  534.                     states["plural"].append("RemoveChannels")
  535.                 elif 'channelfoldertab' in tabTypes:
  536.                     states["folders"].append("RemoveChannels")
  537.                 elif 'addedguidetab' in tabTypes:
  538.                     states["plural"].append("ChannelGuides")
  539.             else:
  540.                 if 'playlisttab' in tabTypes:
  541.                     pass
  542.                 elif 'playlistfoldertab' in tabTypes:
  543.                     states["folder"].append("RemovePlaylists")
  544.                 elif 'channeltab' in tabTypes:
  545.                     pass
  546.                 elif 'channelfoldertab' in tabTypes:
  547.                     states["folder"].append("RemoveChannels")
  548.                 elif 'addedguidetab' in tabTypes:
  549.                     pass
  550.  
  551.         if multiple and is_channel:
  552.             states["plural"].append("UpdateChannels")
  553.  
  554.         actionGroups["ChannelLikeSelected"] = is_channellike and not multiple
  555.         actionGroups["ChannelLikesSelected"] = is_channellike
  556.         actionGroups["PlaylistLikeSelected"] = is_playlistlike and not multiple
  557.         actionGroups["PlaylistLikesSelected"] = is_playlistlike
  558.         actionGroups["ChannelSelected"] = tabTypes.issubset (set(['channeltab'])) and not multiple
  559.         actionGroups["ChannelsSelected"] = tabTypes.issubset (set(['channeltab', 'channelfoldertab']))
  560.         actionGroups["ChannelFolderSelected"] = tabTypes.issubset(set(['channelfoldertab'])) and not multiple
  561.  
  562.         # Handle video item area.
  563.         actionGroups["VideoSelected"] = False
  564.         actionGroups["VideosSelected"] = False
  565.         actionGroups["VideoPlayable"] = False
  566.         videoFileName = None
  567.         if 'downloadeditem' in self.itemListSelection.getTypesDetailed():
  568.             actionGroups["VideosSelected"] = True
  569.             actionGroups["VideoPlayable"] = True
  570.             if len(self.itemListSelection.currentSelection) == 1:
  571.                 actionGroups["VideoSelected"] = True
  572.                 item = self.itemListSelection.getObjects()[0]
  573.                 videoFileName = item.getVideoFilename()
  574.             else:
  575.                 states["plural"].append("RemoveVideos")
  576. #        if len(self.itemListSelection.currentSelection) == 0:
  577. #            if playable_videos:
  578. #                actionGroups["VideoPlayable"] = True
  579.  
  580.         app.controller.frame.onSelectedTabChange(states, actionGroups, 
  581.                 guideURL, videoFileName)
  582.  
  583.     def displayCurrentTabContent(self):
  584.         frame = app.controller.frame
  585.         mainDisplay = frame.getDisplay(frame.mainDisplay)
  586.  
  587.         # Hack to avoid re-displaying channel template
  588.         if (mainDisplay and hasattr(mainDisplay, 'templateName') and mainDisplay.templateName == 'channel'):
  589.             tls = self.tabListSelection
  590.             if len(tls.currentSelection) == 1:
  591.                 for id in tls.currentSelection:
  592.                     tab = tls.currentView.getObjectByID(id)
  593.                     if tab.contentsTemplate == 'channel':
  594.                         newId = int(tab.obj.getID())
  595.                         #print "swapping templates %d %d" % (mainDisplay.kargs['id'], newId)
  596.                                                         
  597.                         self.itemListSelection.clearSelection()
  598.                         self.updateMenus()
  599.                         if mainDisplay.kargs['id'] != newId:
  600.                             mainDisplay.reInit(id = newId)
  601.                         return
  602.         newDisplay = self._chooseDisplayForCurrentTab()
  603.  
  604.         # Don't redisplay the current tab if it's being displayed.  It messes
  605.         # up our database callbacks.  The one exception is the guide tab,
  606.         # where redisplaying it will reopen the home page.
  607.         if (self.lastDisplay and newDisplay == self.lastDisplay and
  608.                 self.lastDisplay is mainDisplay and
  609.                 newDisplay.templateName != 'guide'):
  610.             newDisplay.unlink()
  611.             return
  612.  
  613.         self.itemListSelection.clearSelection()
  614.         self.updateMenus()
  615.         # do a queueSelectDisplay to make sure that the selectDisplay gets
  616.         # executed after our changes to the tablist template.  This makes tab
  617.         # selection feel faster because the selection changes quickly.
  618.         template.queueSelectDisplay(frame, newDisplay, frame.mainDisplay)
  619.         self.lastDisplay = newDisplay
  620.  
  621.     def isTabSelected(self, tab):
  622.         return tab.objID() in self.tabListSelection.currentSelection
  623.  
  624.     def getSelectedTabs(self):
  625.         """Return a list of the currently selected Tabs. """
  626.  
  627.         return self.tabListSelection.getObjects()
  628.  
  629.     def getSelectedItems(self):
  630.         """Return a list of the currently selected items. """
  631.  
  632.         return self.itemListSelection.getObjects()
  633.